--- /dev/null
+/* gtkiconcachevalidator.c
+ * Copyright (C) 2007 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "config.h"
+#include "gtkiconcachevalidator.h"
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+
+#define VERBOSE(x)
+
+#define check(name,condition) \
+ if (!(condition)) \
+ { \
+ VERBOSE(g_print ("bad %s\n", (name))); \
+ return FALSE; \
+ }
+
+static inline gboolean
+get_uint16 (CacheInfo *info,
+ guint32 offset,
+ guint16 *value)
+{
+ if (offset < info->cache_size)
+ {
+ *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
+ return TRUE;
+ }
+ else
+ {
+ *value = 0;
+ return FALSE;
+ }
+}
+
+static inline gboolean
+get_uint32 (CacheInfo *info,
+ guint32 offset,
+ guint32 *value)
+{
+ if (offset < info->cache_size)
+ {
+ *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
+ return TRUE;
+ }
+ else
+ {
+ *value = 0;
+ return FALSE;
+ }
+}
+
+static gboolean
+check_version (CacheInfo *info)
+{
+ guint16 major, minor;
+
+ check ("major version", get_uint16 (info, 0, &major) && major == 1);
+ check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
+
+ return TRUE;
+}
+
+static gboolean
+check_string (CacheInfo *info,
+ guint32 offset)
+{
+ check ("string offset", offset < info->cache_size);
+
+ if (info->flags & CHECK_STRINGS)
+ {
+ gint i;
+ gchar c;
+
+ /* assume no string is longer than 1k */
+ for (i = 0; i < 1024; i++)
+ {
+ check ("string offset", offset + i < info->cache_size)
+ c = *(info->cache + offset + i);
+ if (c == '\0')
+ break;
+ check ("string content", g_ascii_isgraph (c));
+ }
+ check ("string length", i < 1024);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_directory_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 directory_offset;
+ gint i;
+
+ check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
+
+ for (i = 0; i < info->n_directories; i++)
+ {
+ check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
+ if (!check_string (info, directory_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_pixel_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 type;
+ guint32 length;
+
+ check ("offset, pixel data type", get_uint32 (info, offset, &type));
+ check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
+
+ check ("pixel data type", type == 0);
+ check ("pixel data length", offset + 8 + length < info->cache_size);
+
+ if (info->flags & CHECK_PIXBUFS)
+ {
+ GdkPixdata data;
+
+ check ("pixel data", gdk_pixdata_deserialize (&data, length,
+ info->cache + offset + 8,
+ NULL));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_embedded_rect (CacheInfo *info,
+ guint32 offset)
+{
+ check ("embedded rect", offset + 4 < info->cache_size);
+
+ return TRUE;
+}
+
+static gboolean
+check_attach_point_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_attach_points;
+
+ check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
+ check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
+
+ return TRUE;
+}
+
+static gboolean
+check_display_name_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_display_names;
+ gint i;
+
+ check ("offset, display name list",
+ get_uint32 (info, offset, &n_display_names));
+ for (i = 0; i < n_display_names; i++)
+ {
+ if (!check_string (info, offset + 4 + 8 * i))
+ return FALSE;
+ if (!check_string (info, offset + 4 + 8 * i + 4))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_meta_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 embedded_rect_offset;
+ guint32 attach_point_list_offset;
+ guint32 display_name_list_offset;
+
+ check ("offset, embedded rect",
+ get_uint32 (info, offset, &embedded_rect_offset));
+ check ("offset, attach point list",
+ get_uint32 (info, offset + 4, &attach_point_list_offset));
+ check ("offset, display name list",
+ get_uint32 (info, offset + 8, &display_name_list_offset));
+
+ if (embedded_rect_offset != 0)
+ {
+ if (!check_embedded_rect (info, embedded_rect_offset))
+ return FALSE;
+ }
+
+ if (attach_point_list_offset != 0)
+ {
+ if (!check_attach_point_list (info, attach_point_list_offset))
+ return FALSE;
+ }
+
+ if (display_name_list_offset != 0)
+ {
+ if (!check_display_name_list (info, display_name_list_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 pixel_data_offset;
+ guint32 meta_data_offset;
+
+ check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
+ check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
+
+ if (pixel_data_offset != 0)
+ {
+ if (!check_pixel_data (info, pixel_data_offset))
+ return FALSE;
+ }
+ if (meta_data_offset != 0)
+ {
+ if (!check_meta_data (info, meta_data_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image (CacheInfo *info,
+ guint32 offset)
+{
+ guint16 index;
+ guint16 flags;
+ guint32 image_data_offset;
+
+ check ("offset, image index", get_uint16 (info, offset, &index));
+ check ("offset, image flags", get_uint16 (info, offset + 2, &flags));
+ check ("offset, image data offset",
+ get_uint32 (info, offset + 4, &image_data_offset));
+
+ check ("image index", index < info->n_directories);
+ check ("image flags", flags == 1 || flags == 2 || flags == 4 ||
+ flags == 9 || flags == 10 || flags == 12);
+
+ if (image_data_offset != 0)
+ {
+ if (!check_image_data (info, image_data_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_images;
+ gint i;
+
+ check ("offset, image list", get_uint32 (info, offset, &n_images));
+
+ for (i = 0; i < n_images; i++)
+ {
+ if (!check_image (info, offset + 4 + 8 * i))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_icon (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 chain_offset;
+ guint32 name_offset;
+ guint32 image_list_offset;
+
+ check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
+ check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
+ check ("offset, icon image list", get_uint32 (info, offset + 8,
+ &image_list_offset));
+
+ if (!check_string (info, name_offset))
+ return FALSE;
+ if (!check_image_list (info, image_list_offset))
+ return FALSE;
+ if (chain_offset != 0xffffffff)
+ {
+ if (!check_icon (info, chain_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_hash (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_buckets, icon_offset;
+ gint i;
+
+ check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
+
+ for (i = 0; i < n_buckets; i++)
+ {
+ check ("offset, hash chain",
+ get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
+ if (icon_offset != 0xffffffff)
+ {
+ if (!check_icon (info, icon_offset))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * _gtk_icon_cache_validate:
+ * @info: a CacheInfo structure
+ *
+ * Validates the icon cache passed in the @cache and
+ * @cache_size fields of the @info structure. The
+ * validator checks that offsets specified in the
+ * cache do not point outside the mapped area, that
+ * strings look reasonable, and that pixbufs can
+ * be deserialized. The amount of validation can
+ * be controlled with the @flags field.
+ *
+ * Return value: %TRUE if the cache is valid
+ */
+gboolean
+_gtk_icon_cache_validate (CacheInfo *info)
+{
+ guint32 hash_offset;
+ guint32 directory_list_offset;
+
+ if (!check_version (info))
+ return FALSE;
+ check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
+ check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
+ if (!check_directory_list (info, directory_list_offset))
+ return FALSE;
+
+ if (!check_hash (info, hash_offset))
+ return FALSE;
+
+ return TRUE;
+}
+
#include <glib/gstdio.h>
#include <gdk-pixbuf/gdk-pixdata.h>
#include <glib/gi18n.h>
+#include "gtkiconcachevalidator.h"
static gboolean force_update = FALSE;
static gboolean ignore_theme_index = FALSE;
static gboolean quiet = FALSE;
static gboolean index_only = FALSE;
+static gboolean validate = FALSE;
static gchar *var_name = "-";
+/* Quite ugly - if we just add the c file to the
+ * list of sources in Makefile.am, libtool complains.
+ */
+#include "gtkiconcachevalidator.c"
+
#define CACHE_NAME "icon-theme.cache"
#define HAS_SUFFIX_XPM (1 << 0)
return TRUE;
}
+static gboolean
+validate_file (const gchar *file)
+{
+ GMappedFile *map;
+ CacheInfo info;
+
+ map = g_mapped_file_new (file, FALSE, NULL);
+ if (!map)
+ return FALSE;
+
+ info.cache = g_mapped_file_get_contents (map);
+ info.cache_size = g_mapped_file_get_length (map);
+ info.n_directories = 0;
+ info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
+
+ if (!_gtk_icon_cache_validate (&info))
+ {
+ g_mapped_file_free (map);
+ return FALSE;
+ }
+
+ g_mapped_file_free (map);
+
+ return TRUE;
+}
+
static void
build_cache (const gchar *path)
{
exit (1);
}
+ if (!validate_file (tmp_cache_path))
+ {
+ g_printerr (_("The generated cache was invalid.\n"));
+ g_unlink (tmp_cache_path);
+ exit (1);
+ }
+
cache_path = g_build_filename (path, CACHE_NAME, NULL);
#ifdef G_OS_WIN32
{ "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don't include image data in the cache"), NULL },
{ "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
+ { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
{ NULL }
};
path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
#endif
+ if (validate)
+ {
+ gchar *file = g_build_filename (path, CACHE_NAME, NULL);
+
+ if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
+ {
+ if (!quiet)
+ g_printerr (_("File not found: %s\n"), file);
+ exit (1);
+ }
+ if (!validate_file (file))
+ {
+ if (!quiet)
+ g_printerr (_("Not a valid icon cache: %s\n"), file);
+ exit (1);
+ }
+ else
+ {
+ exit (0);
+ }
+ }
+
if (!ignore_theme_index && !has_theme_index (path))
{
g_printerr (_("No theme index file in '%s'.\n"